package org.springextensions.actionscript.context.support
{
	import flash.events.Event;
	
	import org.as3commons.logging.ILogger;
	import org.as3commons.logging.LoggerFactory;
	import org.springextensions.actionscript.ioc.ResourceGroup;
	import org.springextensions.actionscript.ioc.factory.xml.parser.support.nodeparsers.ResourceNodeParser;
	import org.springextensions.actionscript.ioc.factory.xml.parser.support.referenceresolvers.ResourceReferenceResolver;
	import org.springextensions.actionscript.ioc.factory.xml.preprocessors.*;
	import org.springextensions.actionscript.ioc.util.ResourceConstants;
	import org.springextensions.actionscript.mvcs.service.operation.IOperation;
	import org.springextensions.actionscript.mvcs.service.operation.event.AsyncOperationEvent;
	import org.springextensions.actionscript.mvcs.service.operation.events.OperationProgressEvent;
	import org.springextensions.actionscript.mvcs.service.operation.events.ProgressSourceEvent;
	
	public class ResourceAwareApplicationContext extends XMLApplicationContext implements IResourceAwareObjectFactory
	{		
		/**
		 * Logger instance.
		 */
		private static var logger:ILogger = LoggerFactory.getLogger("SpringActionScript");
		
		/**
		 * Creates a new Application Context capable of loading and handling external resources.
		 */
		public function ResourceAwareApplicationContext(source:Object=null)
		{
			super(source);

			// Add the resource-specific preprocessors.
			parser.addPreprocessor(new RequiredResourcesPreprocessor());
			parser.addPreprocessor(new ResourceDefinitionsPreprocessor());
			
			// Add the resource-specific node parser.
			parser.addNodeParser(new ResourceNodeParser(parser));
			
			// Add the resource-specific reference resolver.
			addReferenceResolver(new ResourceReferenceResolver(this));
		}
		
		/**
		 * Returns an IOperation instance that, when executed, will load
		 * the requested resource.
		 * 
		 * The requested resource can be an instance of any class that extends
		 * ResourceDefinition. This can also be a ResourceGroup instance
		 * if you need to load several resources at once.
		 * 
		 * @id the id attribute of the resource object required.
		 * @returns an IOperation instance capable of loading the requested resource. 
		 * Returns null if no resource exists with the supplied id value.
		 */
		public function loadResource(id:String):IOperation
		{
			if (containsObjectDefinition(id))
			{
				return IOperation(getObject(id, [this]));
			}

			return null;
		}
		
		/**
		 * Overrides the usual afterParse() functionality of XMLApplicationContext
		 * in order to load any 'required' resources. These are resources that 
		 * must be present in the ApplicationDomain prior to instantiating any 
		 * non-lazy objects in the Application Context. For instance there may be non-lazy 
		 * objects whose classes are located in a Runtime Shared Library defined
		 * in a resource object. This RSL must be loaded fully before that object
		 * can be instantiated.
		 */
		override protected function afterParse():void 
		{			
			// Grab the resource object with the Master resource id.
			var operation:IOperation = loadResource(ResourceConstants.MASTER_RESOURCE_GROUP_ID);

			if (operation)
			{		
				// If there are required resources then this object adds itself as a listener
				// for operation events and then executes the operation.		
				operation.addEventListener(ProgressSourceEvent.PROGRESS_START, _onRequiredResourceStart);
				operation.addEventListener(OperationProgressEvent.PROGRESS, _onRequiredResourceProgress);
				operation.addEventListener(AsyncOperationEvent.RESULT, _onRequiredResourceLoadComplete);
				operation.execute();
			}
			else
			{
				// Otherwise we just carry on the normal workflow.
				super.afterParse();
			}
		}
		
		/**
		 * Forwards a start event from the required resources.
		 */
		private function _onRequiredResourceStart(e:ProgressSourceEvent):void 
		{
			dispatchEvent(e.clone());
		}
		
		/**
		 * Forwards a progress event from the required resources.
		 */
		private function _onRequiredResourceProgress(e:OperationProgressEvent):void 
		{
			dispatchEvent(e.clone());
		}
		
		/**
		 * Carries on the normal workflow in XMLApplicationContext now
		 * that the required resources are all present.
		 * Forwards a complete event from the required resources.
		 */
		private function _onRequiredResourceLoadComplete(e:AsyncOperationEvent):void 
		{
			super.afterParse();
			
			dispatchEvent(e.clone());
		}
	}
}